package com.woniuxy.service.impl;

import com.github.pagehelper.PageInfo;
import com.woniuxy.entity.DTO.FlashSaleProductDTO;
import com.woniuxy.entity.DTO.FlashSalesDTO;
import com.woniuxy.entity.DTO.QueryAvailableGoodsDTO;
import com.woniuxy.entity.DTO.QueryTimeSaleGoodsDTO;
import com.woniuxy.entity.FlashSaleProducts;
import com.woniuxy.entity.FlashSales;
import com.woniuxy.entity.TimeSaleStatus;
import com.woniuxy.mapper.FlashSalesMapper;
import com.woniuxy.producer.TopicProducer;
import com.woniuxy.service.TimeSaleService;
import com.woniuxy.util.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.text.ParseException;
import java.time.LocalDateTime;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import static com.woniuxy.util.PageParam.executePageQuery;

/**
 * @author ：admin
 * @version ：1.0
 * @date ：Created in 2025/5/14 15:18
 * @description ：TODO
 * @modified_by ：
 */
@Slf4j
@Service
public class TimeSaleServiceImpl implements TimeSaleService {
    @Resource
    private FlashSalesMapper flashSalesMapper;

    @Resource
    private TopicProducer topicProducer;

    @Resource
    private RedisTemplate<String, Object> redisTemplateInit;

    /**
     * <p>查询所有活动状态。</p>
     *
     * @return 活动状态列表
     */
    @Override
    public List<TimeSaleStatus> queryStatusList() {
        return flashSalesMapper.selectTimeSaleStatusList();
    }

    /**
     * <p>查询所有活动基本信息。</p>
     *
     * @param param 分页信息（此处 param 为 null ）
     * @return 活动信息列表
     */
    @Override
    public PageInfo<FlashSalesDTO> queryTimeSaleList(PageParam<FlashSalesDTO> param) {
        return PageParam.executePageQuery(param, p -> flashSalesMapper.selectAllTimeSale());
    }

    /**
     * <p>根据条件分页查询活动信息。</p>
     *
     * @param param 分页信息、查询条件
     * @return 活动信息列表
     */
    @Override
    public PageInfo<FlashSalesDTO> queryTimeSaleByCondition(PageParam<FlashSalesDTO> param) {
        return executePageQuery(param, p -> flashSalesMapper.selectTimeSaleByCondition(param.getParam()));
    }

    /**
     * <p>添加活动信息。</p>
     *
     * @param flashSalesDTO 活动信息及活动商品信息
     */
    @Override
    @Transactional
    public void addTimeSale(FlashSalesDTO flashSalesDTO) {
        // 添加活动基本信息
        FlashSales flashSales = new FlashSales(flashSalesDTO);
        flashSalesMapper.insertTimeSale(flashSales);
        addSaleProducts(flashSalesDTO, flashSales.getSaleId());
    }

    /**
     * <p>查询所有“已上架”状态，且库存不为 0 的商品。</p>
     *
     * @param name 商品名称，允许为空，为空时查询所有商品
     * @return 商品列表
     */
    @Override
    public List<QueryAvailableGoodsDTO> queryAvailableGoods(String name) {
        return flashSalesMapper.selectAllAvailableGoods(name).stream()
                .map(QueryAvailableGoodsDTO::new).collect(Collectors.toList());
    }

    /**
     * <p>根据活动 ID 查询活动详情。</p>
     *
     * @param saleId 活动 ID
     * @return 活动详情信息
     */
    @Override
    public FlashSalesDTO queryTimeSaleDetail(Integer saleId) {
        // 查询活动基本信息
        FlashSalesDTO flashSalesDTO = flashSalesMapper.selectTimeSaleBySaleId(saleId);

        // 查询活动商品列表
        List<FlashSaleProductDTO> flashSaleProductsList = flashSalesMapper.selectTimeSaleProductsBySaleId(saleId);

        // 组装数据
        flashSalesDTO.setMaxPurchase(flashSaleProductsList.get(0).getMaxPurchase());
        flashSalesDTO.setGoodsList(flashSaleProductsList);

        return flashSalesDTO;
    }

    /**
     * <p>修改活动信息。</p>
     * <p>由于此项功能面向后台管理人员，且操作不频繁，因此使用简单粗暴的方案：清空原来的商品列表，插入新的商品列表。</p>
     *
     * @param flashSalesDTO 活动信息
     */
    @Override
    @Transactional
    public void updateTimeSale(FlashSalesDTO flashSalesDTO) {
        // 修改基本信息
        FlashSales flashSales = new FlashSales(flashSalesDTO);
        flashSalesMapper.updateTimeSale(flashSales);

        // 释放库存
        releaseSaleProductsStock(flashSalesDTO.getSaleId());

        // 清空列表
        flashSalesMapper.deleteBatchSaleProductsBySaleId(flashSalesDTO.getSaleId());

        // 重新插入
        addSaleProducts(flashSalesDTO, flashSales.getSaleId());
    }

    /**
     * <p>取消活动。</p>
     *
     * @param saleId 活动 ID
     * @return 是否取消成功
     */
    @Override
    public ResponseData<?> cancelTimeSale(Integer saleId) {
        // 查询活动状态，仅未开始/进行中状态的活动可取消
        FlashSales flashSales = new FlashSales(flashSalesMapper.selectTimeSaleBySaleId(saleId));
        Integer saleStatus = flashSales.getSaleStatus();

        if (saleStatus == 3 || saleStatus == 4) return ResponseData.fail(ResponseEnum.CANCEL_FAIL);

        // 查询 redis 是否记录活动
        String h = "TIME_SALE_PREHEAT";
        FlashSalesDTO flashSalesDTO = (FlashSalesDTO) redisTemplateInit.opsForHash().get(h, saleId + "");

        // 释放库存
        if (saleStatus == 1) { // 活动未开始
            releaseSaleProductsStock(saleId);
        } else { // 活动进行中，库存可能有变化
            // assert flashSalesDTO != null;
            List<FlashSaleProducts> flashSaleProductsList = flashSalesDTO.getGoodsList()
                    .stream().map(FlashSaleProducts::new).collect(Collectors.toList());
            flashSalesMapper.updateBatchPlusStock(flashSaleProductsList);
        }

        // 延迟双删解决缓存数据一致性问题
        redisTemplateInit.opsForHash().delete(h, flashSalesDTO); // 第一次删除 redis 缓存
        flashSales.setSaleStatus(4); // 修改数据库活动状态
        flashSalesMapper.updateCancelTimeSale(flashSales);
        try {
            TimeUnit.MILLISECONDS.sleep(500);
            redisTemplateInit.opsForHash().delete(h, flashSalesDTO); // 第二次删除 redis 缓存
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            log.error("延迟双删中断异常信息：{}", e.getMessage(), e);
        }
        return ResponseData.success();
    }

    /**
     * <p>根据商品名称查询活动商品信息。</p>
     * <p>限时抢购商品数据变更频繁，实时性要求高，因此查询时不使用 redis 缓存。</p>
     *
     * @param searchKey 用户输入的商品名称
     * @return 活动商品信息列表
     */
    @Override
    public List<QueryTimeSaleGoodsDTO> queryTimeSaleByGoodsName(String searchKey) throws ParseException {
        Integer userId = UserContext.getUserId();
        //Integer userId = 1;
        return flashSalesMapper.selectTimeSaleGoodsByGoodsName(userId, searchKey);
    }

    /**
     * <p>保存抢购页面添加购物车信息。</p>
     *
     * @param queryTimeSaleGoodsDTOList 购物车信息
     */
    @Override
    public void saveCart(List<QueryTimeSaleGoodsDTO> queryTimeSaleGoodsDTOList) {
        // 通过消息队列将购物车信息插入数据库
        queryTimeSaleGoodsDTOList.forEach(p -> {
            try {
                p.setUserId(UserContext.getUserId());
            } catch (ParseException e) {
                throw new RuntimeException(e);
            }
        });
        topicProducer.sendTopic(queryTimeSaleGoodsDTOList, RabbitMQConst.SAVE_CART_ROUTING_KEY);
    }

    /**
     * <p>添加活动商品到数据库。</p>
     *
     * @param flashSalesDTO 活动信息
     * @param saleId 活动 ID
     */
    private void addSaleProducts(FlashSalesDTO flashSalesDTO, Integer saleId) {
        // 添加活动商品
        List<FlashSaleProducts> flashSaleProductsList = flashSalesDTO.getGoodsList()
                .stream().map(flashSaleProductDTO -> {
                    flashSaleProductDTO.setSaleId(saleId);
                    flashSaleProductDTO.setMaxPurchase(flashSalesDTO.getMaxPurchase());
                    return new FlashSaleProducts(flashSaleProductDTO);
                }).collect(Collectors.toList());
        topicProducer.sendTopic(flashSaleProductsList, RabbitMQConst.ADD_TIME_SALE_ROUTING_KEY);
    }

    /**
     * <p>释放活动商品库存</p>
     *
     * @param saleId 活动 ID
     */
    private void releaseSaleProductsStock(Integer saleId) {
        List<FlashSaleProducts> flashSaleProductsList =
                flashSalesMapper.selectTimeSaleProductsBySaleId(saleId)
                        .stream()
                        .map(FlashSaleProducts::new)
                        .collect(Collectors.toList());
        flashSalesMapper.updateBatchPlusStock(flashSaleProductsList);
    }

    /**
     * 定时任务：检查即将开始的活动，通过消息队列存入缓存
     */
    @Scheduled(fixedRate = 60 * 1000) // 每分钟执行一次
    public void checkUpcomingActivities() {
        // 获取未来1小时内所有“未开始”状态的活动
        LocalDateTime now = LocalDateTime.now();
        LocalDateTime oneHourLater = now.plusHours(1);
        List<FlashSalesDTO> flashSaleDTOList = flashSalesMapper.findByStartTimeBetween(now, oneHourLater);

        // 发送消息到限时抢购消息队列
        flashSaleDTOList.forEach(flashSalesDTO -> topicProducer.sendTopic(flashSalesDTO,
                RabbitMQConst.TIME_SALE_PREHEAT_ROUTING_KEY));
    }
}
